#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <functional>
#include <mutex>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpServer.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/base/CountDownLatch.h>
#include "abstract.hpp"
#include "common.hpp"
#include "message.hpp"

namespace MyRpc
{
    // Buffer类直接全部使用muduo库中的Buffer类
    class Buffer : public BaseBuffer
    {
    public:
        Buffer(muduo::net::Buffer* buf)
            :_buf(buf)
        {}
        using ptr = std::shared_ptr<Buffer>;
        virtual size_t readableBytes() override
        {
            return _buf->readableBytes();
        }
        // peek 提供的接口是为了尝试查看数据是否够 不读取数据
        virtual int32_t peekInt32() override
        {
            return _buf->peekInt32();
        }
        // read 接口直接是读取数据 并且删除读取的数据 peek + retrieve 接口
        virtual int32_t readInt32() override
        {
            return _buf->readInt32();
        }
        // retrieve 接口是为了删除数据
        virtual void retrieveInt32() override
        {
            _buf->retrieveInt32();
        }
        
        virtual std::string retrieveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }
    private:
        // 这里直接使用muduo库里面的数据缓冲区
        muduo::net::Buffer* _buf;
    };

    // 实现一个工厂类 用来直接构造Buffer对象

    class BufferFactory
    {
    public:
        // 用参数包传入构造Buffer对象时候所传入的参数
        template<class ...Args>
        static BaseBuffer::ptr create(Args&& ...args)
        {
            return std::make_shared<Buffer>(std::forward<Args>(args)...);
        }
    };

    // 消息协议定制
    class Protocol : public BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<Protocol>;
        // 对消息进行判断是否可以处理
        virtual bool canProcessed(const BaseBuffer::ptr& buf) override
        {
            if(buf->readableBytes() < Lsize)
            {
                // 缓冲区中的数据不足4字节读取
                return false;
            }
            // 首先读取首部4字节长度 确定LV格式中的length
            int32_t length = buf->peekInt32();
            // 表示现在缓冲区中一共有多少数据
            int32_t total_length = buf->readableBytes();
            if(Lsize + length <= total_length)
            {
                // 表示可以读取数据
                return true;
            }
            else
                return false;
        }
        // 对msg中的消息进行序列化 并且返回 |--total_length--|--MType--|--Idlength--|--id--|--body--|  格式的数据
        virtual std::string serialize(const BaseMessage::ptr& msg) override
        {
            // 这里读取的数据是正常字节序 为了能使用我们要把正常的字节序转化为网络字节序 才可以发送给对方
            int32_t mtype = ::htonl((int)msg->mtype());
            // std::cout << "mtype :" << (int)msg->mtype() << "\n";
            // std::cout << "host_mtype: " << mtype << "\n";
            std::string id = msg->id();
            // LOG(DEBUGLEVEL, "id : %s", id.c_str());
            // std::cout << "id : " <<  id << "\n";
            int32_t idsize = ::htonl(id.size());
            // std::cout << "idsize : " << id.size() << "\n";
            // std::cout << "host_idsize : " << idsize << "\n";
            std::string body;
            bool flag = msg->serialize(body);
            // std::cout << body << "\n";
            if(flag == false)
            {
                LOG(DEBUGLEVEL, "数据序列化失败!"); 
                return std::string();
            }
            int total_length = MType_length + IDlength + id.size() + body.size();
            // std::cout << "totol_length : " << totol_length << "\n";
            int32_t length = ::htonl(total_length);
            // std::cout << "host_length: " << length << std::to_string(length) <<  "\n";
            std::string res;
            res.reserve(total_length);
            // res = std::to_string(length) + std::to_string(mtype) + std::to_string(idsize) + id + body;
            // 将数据大小作为四字节数据放入到LV格式中
            res.append((char*)&length, Lsize);
            res.append((char*)&mtype, MType_length);
            res.append((char*)&idsize, IDlength);
            res += id;
            res += body;

            // std::cout << res << "\n";
            return res;
        }

        // 对buf中的数据进行处理的放到msg对象中函数
        virtual bool onMessage(const BaseBuffer::ptr& buf, BaseMessage::ptr& msg) override
        {
            // 来到这个函数里面说明至少有一条消息可以被处理
            // 在读取数据的时候read接口自己会把网络序列转化为主机序列
            // 读取总的一条消息的大小
            int32_t length = buf->readInt32();
            // 读取mtype的数据
            MType mtype = (MType)buf->readInt32();
            // 读取IDlength的数据
            int32_t idlength = buf->readInt32();
            // 读取id信息
            std::string id = buf->retrieveAsString(idlength);
            int32_t body_size = length - MType_length - IDlength - idlength;
            // 读取body的数据
            std::string body = buf->retrieveAsString(body_size);
            // 创建mtype类型的Message对象
            msg = MessageFactory::create(mtype);
            // 填充创建的BaseMessage对象
            msg->setId(id);
            msg->setMType(mtype);
            // 把body中的数据反序列化到msg中
            bool flag = msg->unserialize(body);
            if(flag == false)
            {
                LOG(DEBUGLEVEL, "数据反序列化失败!");
                return false;
            }
            return true;
        }
    private:
        const size_t Lsize = 4;
        const size_t MType_length = 4;
        const size_t IDlength = 4;
    };

    class ProtocolFactory
    {
    public:
        // 用参数包传入构造Buffer对象时候所传入的参数
        template<class ...Args>
        static BaseProtocol::ptr create(Args&& ...args)
        {
            return std::make_shared<Protocol>(std::forward<Args>(args)...);
        }
    };

    // 连接管理 提供发送消息的接口
    class Connection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<Connection>;
        Connection(const muduo::net::TcpConnectionPtr& conn, const BaseProtocol::ptr& protocol)
            :_conn(conn)
            ,_protocol(protocol)
        {

        }
        // 这里需要把BaseMessage类型的消息经过protocol序列化为LV格式之后再发送过去
        virtual bool send(const BaseMessage::ptr& msg) override
        {
            // 把BaseMessage 中的数据发送过去
            std::string str = _protocol->serialize(msg);
            if(str == std::string())
            {
                LOG(DEBUGLEVEL, "消息序列化为LV格式失败!");
                return false;
            }
            _conn->send(str);
            return true;
        }
        virtual void shutdown() override
        {
            _conn->shutdown();
        }
        virtual bool connected() override
        {
            return _conn->connected();
        }
    private:
        muduo::net::TcpConnectionPtr _conn;
        BaseProtocol::ptr _protocol;
    };

    class ConnectionFactory
    {
    public:
        template<class ...Args>
        static BaseConnection::ptr create(Args&& ...args)
        {
            return std::make_shared<Connection>(std::forward<Args>(args)...);
        }
    };

    // 服务端类 创建一个TcpServer对象
    class Server : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<Server>;
        Server(int32_t port)
            :_server(&_loop, muduo::net::InetAddress("127.0.0.1", port), "TcpServer", muduo::net::TcpServer::Option::kReusePort)
            ,_protocol(ProtocolFactory::create()) // 创建一个protocol用来序列化消息为LV格式
        {
            // 设置这几个必须要的函数
            _server.setConnectionCallback(std::bind(&Server::connectionCallBack, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&Server::messageCallBack, this, 
                std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        }

        virtual void start() override
        {
            // 启动tcp服务和事件循环监控loop服务
            _server.start();
            _loop.loop();
        }
    private:
        void connectionCallBack(const muduo::net::TcpConnectionPtr& conn)
        {
            // 判断是否连接成功
            if(conn->connected())
            {
                {
                    std::lock_guard<std::mutex> lockguard(_mtx);
                    auto it = _conns.find(conn);
                    if(it == _conns.end())
                    {
                        // 说明没有找到 把这个新的conn插入进去
                            BaseConnection::ptr connection = ConnectionFactory::create(conn, _protocol);
                            _conns.insert(std::make_pair(conn, connection));
                            LOG(DEBUGLEVEL, "新的客户端插入成功!");
                    }
                    if(_conn_cb)
                        _conn_cb(it->second);
                }
            }
            else
            {
                // 这里代表连接失败 就关闭连接 并且清除 _conns中的数据
                LOG(DEBUGLEVEL, "连接失败 关闭连接!");
                auto it = _conns.find(conn);
                if(it == _conns.end())
                {
                    return;
                }
                else
                {
                    _conns.erase(conn);
                    if(_close_cb)
                        _close_cb(it->second);
                }
                conn->shutdown();
            }
        }
        void messageCallBack(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp te)
        {
            // LOG(DEBUGLEVEL, "消息接收成功!");
            BaseBuffer::ptr buffer = BufferFactory::create(buf);  // 把消息放入自己的Buffer类中
            BaseConnection::ptr connptr;
            // 底层有Connection消息就绪 触发消息回调
            // 通过conn找到Connection::ptr 来调用消息发送接口
            {
                std::lock_guard<std::mutex> lockguard(_mtx);
                auto it = _conns.find(conn);
                if(it == _conns.end())
                {
                    LOG(DEBUGLEVEL, "没有找到连接 消息处理失败 关闭连接!");
                    conn->shutdown();
                    if(_close_cb)
                        _close_cb(it->second);
                    return;
                }
                // 找到了这个connection
                // LOG(DEBUGLEVEL, "有消息可以处理!");
                connptr = it->second;
            }
            // 消息足够多的话 用while循环一直发送
            while(1)
            {
                if(_protocol->canProcessed(buffer) == false)
                {
                    // 缓冲区消息出现错误 关闭连接
                    if(buffer->readableBytes() > (1 << 16))
                    {
                        LOG(DEBUGLEVEL, "缓冲区消息出现问题 不能被处理!");
                        conn->shutdown();
                        return;
                    }
                    break;
                }
                // 走到这块说明消息足够可以被解析
                BaseMessage::ptr msg;
                // 处理消息的时候 onMessage可以自己创建BaseMessage对象
                _protocol->onMessage(buffer, msg);
                // 然后再由Connection对象进行发送消息
                // connptr->send(msg);
                // 调用messagecallback函数
                if(_msg_cb) 
                {
                    // LOG(DEBUGLEVEL, "有消息可以被处理!");
                    _msg_cb(connptr, msg);
                }
            }
        }
    private:
        std::mutex _mtx;
        muduo::net::EventLoop _loop;
        muduo::net::TcpServer _server;
        BaseProtocol::ptr _protocol;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;  // _conns 用来储存所有的Connection信息
    };

    class ServerFactory
    {
    public:
        template<class ...Args>
        static BaseServer::ptr create(Args&& ...args)
        {
            return std::make_shared<Server>(std::forward<Args>(args)...);
        }
    };

    // 客户端类
    class Client : public BaseClient
    {
    public:
        Client(const std::string& ip, const int& port)
            :_eventloop(_loop.startLoop())
            ,_protocol(ProtocolFactory::create())
            ,_client(_eventloop, muduo::net::InetAddress(ip, port), "Client")
            ,_countdown_latch(1)
        {}
        using ptr = std::shared_ptr<Client>;
        // 连接服务端函数
        virtual void connect() override
        {
            _client.setConnectionCallback(std::bind(&Client::connCallBack, this, std::placeholders::_1));
            _client.setMessageCallback(std::bind(&Client::mesCallBack, this, std::placeholders::_1,
                     std::placeholders::_2, std::placeholders::_3));
            _client.connect();
            _countdown_latch.wait();
        }
        // 发送消息函数
        virtual bool send(const BaseMessage::ptr& msg) override
        {
            // 用_prorocol协议把msg中的信息转化为LV格式的消息 然后进行发送
            bool flag = _conn->send(msg);
            if(flag == false)
            {
                LOG(DEBUGLEVEL, "消息发送失败!");
                return false;
            }
            LOG(DEBUGLEVEL, "消息发送成功!");
            return true;    
        }
        virtual void shutdown() override
        {
            // 关闭客户端
            _client.disconnect();
        }
        virtual bool connected() override
        {
            return (_conn->connected() && _conn);
        }
        // 获取连接对象
        virtual BaseConnection::ptr connection() override
        {
            return _conn;
        }
    private:
        void connCallBack(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                LOG(DEBUGLEVEL, "连接成功!");
                _conn = ConnectionFactory::create(conn, _protocol);     // 把这个conn给到自己
                _countdown_latch.countDown();  // 让计数器减减 唤醒阻塞client
            }
            else
            {
                LOG(DEBUGLEVEL, "连接失败 连接关闭!");
                _conn.reset();
            }
        }

        void mesCallBack(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp timestamp)
        {
            // 收到消息之后的回调函数
            // LOG(DEBUGLEVEL, "接收到服务端的消息!");
            BaseBuffer::ptr buffer = BufferFactory::create(buf);
            while(1)
            {
                // 一直获取收到的信息直到收取完毕
                if(_protocol->canProcessed(buffer) == false)
                {
                    // 如果缓冲区中的数据大于 1 << 16 的数据还不能被处理 说明缓冲区出现问题了
                    if(buffer->readableBytes() > (1 << 16))
                    {
                        // 缓冲区出现消息错误
                        LOG(DEBUGLEVEL, "缓冲区出现错误 关闭连接!");
                        _conn->shutdown();
                        return;
                    }
                    break;
                }
                // 走到这块说明可以至少有一条信息可以被处理
                if(_msg_cb)
                {
                    BaseMessage::ptr msg;
                    // 把buffer中的信息取出一个放到msg中
                    _protocol->onMessage(buffer, msg);
                    // 调用消息的回调函数对消息进行应用层使用
                    _msg_cb(_conn, msg);
                }
            }
        }
    private:
        // 因为客户端无法自己访问连接完毕之后创建的TcpConnection对象，所以在此我们进行手动设置一个这个对象
        BaseConnection::ptr _conn;  
        muduo::net::EventLoopThread _loop;
        muduo::net::EventLoop* _eventloop;
        BaseProtocol::ptr _protocol;
        muduo::net::TcpClient _client;
        muduo::CountDownLatch _countdown_latch;   // 用来等待客户端连接成功再进行发送消息
    };

    class ClientFactory
    {
    public:
        template<class ...Args>
        static BaseClient::ptr create(Args&& ...args)
        {
            // forward 是一个模板类型
            return std::make_shared<Client>(std::forward<Args>(args)...);
        }
    };
}